﻿# -*- coding: utf-8 -*-
#
#  Mar. 29, 2008
#
#  The author disclaims copyright to this source code.
#  In place of a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#                                         Stolen from SQLite :-)
#  Any feedback is welcome.
#  Kohei TAKETA <k-tak@void.in>
#
# depends on:
#   pycurl
#       http://pycurl.sourceforge.net/
#   decorator
#       http://www.phyast.pitt.edu/~micheles/python/documentation.html
#
__all__ = [
    "Request",
    "Response",
    "AsyncTask",
    "ConnectionError",
    "request",
    "request_async",
    "parse_http_headers"
]

import pycurl
from urllib import quote_plus
from threading import Lock, Thread
from decorator import decorator
try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO


AUTH_ANY = pycurl.HTTPAUTH_ANY
AUTH_ANYSAFE = pycurl.HTTPAUTH_ANYSAFE
AUTH_BASIC = pycurl.HTTPAUTH_BASIC
AUTH_DIGEST = pycurl.HTTPAUTH_DIGEST
AUTH_NTLM = pycurl.HTTPAUTH_NTLM


@decorator
def _state_locked(func, *args, **kw):
    self = args[0]
    self._state_lock.acquire()
    try:
        result = func(*args, **kw)
    finally:
        self._state_lock.release()

    return result


class Request(object):
    """
    リクエストに関する情報を表すクラス。
    """
    _curl = None
    _max_header_size = None
    _max_body_size = None
    _current_header_size = None
    _current_body_size = None
    _header_out = None
    _body_out = None
    
    def __init__(self, url=None):
        """
        Requestオブジェクトを構築する。
        
        @param url: URL
        @type url: str/unicode
        """
        self._curl = pycurl.Curl()
        self._curl.setopt(pycurl.NOPROGRESS, 1)
        self._curl.setopt(pycurl.NOSIGNAL, 1)
        self._curl.setopt(pycurl.COOKIEFILE, "")
        self._curl.setopt(pycurl.OPT_FILETIME, 1)
        self._curl.setopt(pycurl.HTTP_CONTENT_DECODING, 1)
        self._curl.setopt(pycurl.ENCODING, "")
        self._curl.setopt(pycurl.SSL_VERIFYPEER, 0)
        self._max_header_size = 1 << 63
        self._max_body_size = 1 << 63

        if (url != None):
            self.set_url(url)

    def __del__(self):
        self.close()

    def close(self):
        """
        Requestオブジェクトを破棄する。libcurlのリソースを解放する。
        """
        if (self._curl):
            self._curl.close();
            self._curl = None

    def set_url(self, url):
        """
        アクセス対象のURLを指定する。
        
        @param url: URL
        @type url: str/unicode
        """
        if (isinstance(url, unicode)):
            url = url.encode("utf-8", "ignore")
        self._curl.setopt(pycurl.URL, url)

    def set_method(self, method):
        """
        使用するHTTPメソッドを指定する。
        
        @param method: "GET","HEAD",もしくは"POST"
        @type method: str/unicode
        """
        m = str(method).upper()
        if (m == "GET"):
            self._curl.setopt(pycurl.HTTPGET, 1)
        elif (m == "POST"):
            self._curl.setopt(pycurl.POST, 1)
        elif (m == "HEAD"):
            self._curl.setopt(pycurl.NOBODY, 1)

    def set_headers(self, headers):
        """
        サーバに送信するHTTPヘッダを指定する。
        
        @param headers: ヘッダ文字列のリスト
        @type headers: list
        """
        self._curl.setopt(pycurl.HTTPHEADER, headers)

    def set_header_output_file(self, f):
        """
        応答メッセージヘッダを書き込むファイルライクオブジェクトを指定する。
        
        @param f: メッセージヘッダを書き込むファイルライクオブジェクト
        @type f: file-like object
        """
        self._header_out = f

    def set_body_output_file(self, f):
        """
        応答メッセージボディを書き込むファイルライクオブジェクトを指定する。
        
        @param f: メッセージボディを書き込むファイルライクオブジェクト
        @type f: file-like object
        """
        self._body_out = f

    def set_post_fields(self, data):
        """
        送信するPOSTデータを指定する。
        このメソッドを呼ぶと、HTTPメソッドがPOSTに変更される。
        
        @param data: 送信するフィールドのデータを保持する辞書オブジェクト。
                     辞書のキーがポストフィールドの名前、辞書の値がポストフィールドの値になる。
        @type data: dict (str -> str)
        """
        out = StringIO()
        for k, v in data.iteritems():
            entry = quote_plus(k) + "=" + quote_plus(v)
            if (out.tell() > 0):
                out.write("&")
            out.write(entry)

        fields = out.getvalue()
        out.close()
        self._curl.setopt(pycurl.POSTFIELDS, fields)
        self._curl.setopt(pycurl.POSTFIELDSIZE_LARGE, len(fields))

    def set_cookie_list(self, cookies):
        """
        サーバに送信するCookieを指定する。

        @param cookies:  Cookieリスト。Response#get_cookie_list()で取得したデータを
                        そのまま指定できる。
        @type cookies: list
        @note: cookiesは、以下の形式の文字列のリストである（以下一行で）。
               ドメイン[TAB]末尾マッチ[TAB]パス[TAB]セキュア[TAB]
               失効タイムスタンプ[TAB]名前[TAB]値
        """
        self._curl.setopt(pycurl.COOKIELIST, "\n".join(cookies))

    def set_connect_timeout(self, timeout):
        """
        接続タイムアウト秒数を指定する。
        
        @param timeout: タイムアウト(sec)
        @type timeout: int/float
        """
        self._curl.setopt(pycurl.CONNECTTIMEOUT, timeout)

    def set_timeout(self, timeout):
        """
        トータルのタイムアウト秒数を指定する。
        
        @param timeout: タイムアウト(sec)
        @type timeout: int/float
        """
        self._curl.setopt(pycurl.TIMEOUT, timeout)

    def set_max_header_size(self, size):
        """
        ダウンロードするヘッダの最大サイズを指定する。
        
        @param size: ヘッダの最大サイズ(bytes)
        @type size: int/long
        @note:
            ダウンロードしたヘッダのデータ量がsizeを超えた時点で、
            ダウンロードは失敗し、コールバック関数failが呼ばれる。
        """
        self._max_header_size = size

    def set_max_body_size(self, size):
        """
        ダウンロードするメッセージボディの最大サイズを指定する。
        
        @param size: メッセージボディの最大サイズ(bytes)
        @type size: int/long
        @note:
            ダウンロードしたメッセージボディのデータ量がsizeを超えた時点で、
            ダウンロードは失敗し、コールバック関数failが呼ばれる。
        """
        self._max_body_size = size

    def set_user_agent(self, useragent):
        """
        ユーザエージェントを指定する。
        
        @param useragent: ユーザエージェント文字列
        @type useragent: str/unicode
        """
        if (isinstance(useragent, unicode)):
            useragent = useragent.encode("utf-8", "ignore")
        self._curl.setopt(pycurl.USERAGENT, useragent)

    def set_follow_location(self, count):
        """
        Locationヘッダを追跡する最大回数を指定する。
        
        @param count: Locationヘッダを追跡する最大回数。0を指定すると追跡しない。
        @type count: int
        """
        if (count > 0):
            self._curl.setopt(pycurl.FOLLOWLOCATION, 1)
            self._curl.setopt(pycurl.MAXREDIRS, count)
        else:
            self._curl.setopt(pycurl.FOLLOWLOCATION, 0)

    def set_referer(self, referer):
        """
        サーバに送信するRefererヘッダの値を指定する。
        
        @param referer: Refererヘッダの値
        @type referer: str/unicode
        """
        if (isinstance(referer, unicode)):
            referer = referer.encode("utf-8", "ignore")
        self._curl.setopt(pycurl.REFERER, referer)

    def set_proxy(self, hostport, user=None, passwd=None, authtype=AUTH_ANY):
        """
        HTTPプロキシの情報を指定する。
        
        @param hostport: "hostname:portnumber"形式の文字列
        @type hostport: str
        @param user: ユーザ名
        @type user: str
        @param passwd: パスワード
        @type passwd: str
        @param authtype: 使用する認証スキーム。AUTH_*定数のビット和を指定。
        @type authtype: int
        """
        if (hostport != None):
            self._curl.setopt(pycurl.PROXY, hostport)
            if (user != None):
                self._curl.setopt(pycurl.PROXYAUTH, authtype)
                if (passwd != None):
                    self._curl.setopt(pycurl.PROXYUSERPWD,
                                      user + ":" + passwd)
                else:
                    self._curl.setopt(pycurl.PROXYUSERPWD, user)
        else:
            self._curl.setopt(pycurl.PROXY, "")

    def set_auth(self, user, passwd, authtype=AUTH_ANY):
        """
        HTTP認証情報を指定する。
        
        @param user: ユーザ名
        @type user: str
        @param passwd: ユーザ名
        @type passwd: str
        @param authtype: 使用する認証スキーム。AUTH_*定数のビット和を指定。
        @type authtype: int
        """
        if (user != None):
            self._curl.setopt(pycurl.HTTPAUTH, authtype)
            if (passwd != None):
                self._curl.setopt(pycurl.USERPWD, user + ":" + passwd)
            else:
                self._curl.setopt(pycurl.USERPWD, user)

    def set_encoding(self, encoding):
        """
        リクエストメッセージで送信するAccept-Encodingヘッダの値を指定する。
        
        @param encoding: "gzip", "deflate", "identity"の任意の組み合わせ
        @type encoding: str
        """
        self._curl.setopt(pycurl.ENCODING, encoding)

    def set_range(self, contentrange):
        """
        リクエストメッセージで送信するRangeヘッダの値を指定する。
        
        @param contentrange:
            "N-M"の型式の文字列。Nは開始バイト位置、Mは終了バイト位置。
            Mの位置も範囲に含まれる。
            X-Y,N-Mのように、複数のレンジをカンマで区切って指定することもできる。
            この場合、サーバはMIMEマルチパートメッセージを返すであろう。
        @type contentrange: str
        """
        self._curl.setopt(pycurl.RANGE, contentrange)

    def set_ssl_verifypeer(self, truefalse):
        """
        SSL通信において、ピアの真正性を検証するかどうかを指定する。
        
        @param truefalse:
              Trueなら検証する。Falseなら検証しない。
              検証する場合は、set_cainfoで、検証に用いる証明書ストアを
              指定すること。
        @type truefalse: bool
        """
        if (truefalse):
            self._curl.setopt(pycurl.SSL_VERIFYPEER, 1)
        else:
            self._curl.setopt(pycurl.SSL_VERIFYPEER, 0)

    def set_cainfo(self, cainfo):
        """
        ピアの真正性を検証するための証明書ストアを指定する。
        
        @param cainfo: 証明書ファイルのパス
        @type cainfo: str
        """
        self._curl.setopt(pycurl.CAINFO, cainfo)

    def _write_header(self, data):
        self._header_out.write(data)
        self._current_header_size += len(data)
        if (self._current_header_size > self._max_header_size):
            return 0
        else:
            return len(data)

    def _write_body(self, data):
        self._body_out.write(data)
        self._current_body_size += len(data)
        if (self._current_body_size > self._max_body_size):
            return 0
        else:
            return len(data)

    def _pre_perform(self):
        if (self._header_out == None):
            self._header_out = StringIO()

        if (self._body_out == None):
            self._body_out = StringIO()

        self._curl.setopt(pycurl.HEADERFUNCTION, self._write_header)
        self._curl.setopt(pycurl.WRITEFUNCTION, self._write_body)
        self._current_header_size = 0
        self._current_body_size = 0

    def _get_curl(self):
        return self._curl

    def _get_header_output_file(self):
        return self._header_out

    def _get_body_output_file(self):
        return self._body_out


class Response(object):
    """
    サーバからの応答を表すクラス
    """
    _request = None
    _curl = None

    def __init__(self, req):
        """
        Responseオブジェクトを構築する。ユーザが直接呼び出すことはない。
        
        @param req: Requestオブジェクト
        @type req: Request
        """
        self._request = req
        self._curl = req._get_curl()

    def __del__(self):
        self.close()

    def close(self):
        """
        Responseオブジェクトを破棄する。libcurlのリソースを解放する。
        """
        self._curl = None
        if (self._request):
            self._request.close()
            self._request = None

    def detach(self):
        """
        内部で保持しているReqeustオブジェクトを切り離して返す。
        返されたRequestオブジェクトは、再利用することができる。
        Responseオブジェクト自体は利用不要になる。
        
        @return: Requestオブジェクト
        @rtype: Request
        """
        self._curl = None
        req = self._request
        self._request = None
        return req

    def get_content(self):
        """
        レスポンスメッセージボディをバイト文字列として取得する。
        Request#set_body_output_fileで出力先ファイルを指定した場合は
        Noneが返される。
        
        @return: レスポンスメッセージボディ
        @rtype: str
        """
        outf = self._request._get_body_output_file()
        if (hasattr(outf, "getvalue")):
            return outf.getvalue()
        else:
            return None

    def get_headers(self):
        """
        レスポンスメッセージヘッダをバイト文字列として取得する。
        Request#set_header_output_fileで出力先ファイルを指定した場合は
        Noneが返される。
        
        @return: レスポンスメッセージヘッダ
        @rtype: str
        """
        outf = self._request._get_header_output_file()
        if (hasattr(outf, "getvalue")):
            return outf.getvalue()
        else:
            return None

    def get_status_code(self):
        """
        HTTPステータスコードを取得する。
        
        @return: HTTPステータスコード
        @rtype: int
        """
        return self._curl.getinfo(pycurl.HTTP_CODE)

    def get_effective_url(self):
        """
        最終的なURLを取得する。Locationヘッダを追跡した場合は、
        Request#set_urlで指定したURLと異なる。
        
        @return: 最終的なURL
        @rtype: str
        """
        return self._curl.getinfo(pycurl.EFFECTIVE_URL)

    def get_cookie_list(self):
        """
        Cookieリストを取得する。
        
        @return: Cookieリスト
        @rtype: list
        """
        return self._curl.getinfo(pycurl.INFO_COOKIELIST)

    def get_filetime(self):
        """
        URLの最終変更時刻を取得する。
        
        @return: 最終変更時刻
        @rtype: float
        """
        return self._curl.getinfo(pycurl.INFO_FILETIME)

    def get_total_time(self):
        """
        URLの取得に要したトータルの時間を秒数で取得する。
        
        @return: URLの取得に要したトータルの時間(sec)
        @rtype: float
        """
        return self._curl.getinfo(pycurl.TOTAL_TIME)

    def get_namelookup_time(self):
        """
        名前解決に要した時間を秒数で取得する。
        
        @return: 名前解決に要した時間(sec)
        @rtype: float
        """
        return self._curl.getinfo(pycurl.NAMELOOKUP_TIME)

    def get_connect_time(self):
        """
        サーバに接続するのに要した時間を秒数で取得する。
        
        @return: サーバに接続するのに要した時間(sec)
        @rtype: float
        """
        return self._curl.getinfo(pycurl.CONNECT_TIME)

    def get_pretransfer_time(self):
        return self._curl.getinfo(pycurl.PRETRANSFER_TIME)

    def get_starttransfer_time(self):
        return self._curl.getinfo(pycurl.STARTTRANSFER_TIME)

    def get_redirect_time(self):
        return self._curl.getinfo(pycurl.REDIRECT_TIME)

    def get_redirect_count(self):
        return self._curl.getinfo(pycurl.REDIRECT_COUNT)

    def get_redirect_url(self):
        return self._curl.getinfo(pycurl.REDIRECT_URL)

    def get_size_upload(self):
        return self._curl.getinfo(pycurl.SIZE_UPLOAD)

    def get_size_download(self):
        """
        実際にダウンロードされたバイト数を取得する。
        """
        return self._curl.getinfo(pycurl.SIZE_DOWNLOAD)

    def get_speed_upload(self):
        return self._curl.getinfo(pycurl.SPEED_UPLOAD)

    def get_speed_download(self):
        return self._curl.getinfo(pycurl.SPEED_DOWNLOAD)

    def get_header_size(self):
        return self._curl.getinfo(pycurl.HEADER_SIZE)

    def get_request_size(self):
        return self._curl.getinfo(pycurl.REQUEST_SIZE)

    def get_content_length_download(self):
        """
        レスポンスメッセージのContent-Lengthヘッダの値を取得する。
        """
        return self._curl.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD)

    def get_content_length_upload(self):
        return self._curl.getinfo(pycurl.CONTENT_LENGTH_UPLOAD)

    def get_content_type(self):
        """
        レスポンスメッセージのContent-Typeヘッダの値を取得する。
        """
        return self._curl.getinfo(pycurl.CONTENT_TYPE)

    def get_httpauth_avail(self):
        """
        サーバによって指定された、利用可能な認証スキームを返す。
        """
        return self._curl.getinfo(pycurl.HTTPAUTH_AVAIL)

    def get_proxyauth_avail(self):
        """
        プロキシによって指定された、利用可能な認証スキームを返す。
        """
        return self._curl.getinfo(pycurl.PROXYAUTH_AVAIL)

    def get_os_errno(self):
        return self._curl.getinfo(pycurl.OS_ERRNO)


class AsyncTask(object):
    STATE_INIT = 0
    STATE_RUNNING = 1
    STATE_STOPPING = 2
    STATE_STOPPED = 3
    STATE_CLOSED = 4

    _state_lock = None
    _multi_lock = None
    _state = None
    _thread = None
    _multi = None
    _request = None
    _succeed = None
    _fail = None

    def __init__(self, req, succeed, fail):
        """
        リクエストを非同期に実行するためのオブジェクトを構築する。
        
        @param req: リクエストオブジェクト
        @type req: Request
        @param succeed: リクエスト成功時に呼び出されるコールバック関数
        @type succeed: function
        @param fail: リクエスト失敗時に呼び出されるコールバック関数
        @type fail: function
        @note:
            succeed,failのシグニチャは以下のとおり。
            def succeed(response)
            def fail(response, errcode, errmsg)
        """
        self._state_lock = Lock()
        self._multi_lock = Lock()
        self._state = AsyncTask.STATE_INIT
        self._multi = pycurl.CurlMulti()
        self._request = req
        self._succeed = succeed
        self._fail = fail

        req._pre_perform()
        self._multi.add_handle(req._get_curl())
        self._start()

    def __del__(self):
        self.close()

    @_state_locked
    def close(self):
        try:
            self._cancel()
        except:
            pass

        if (self._multi != None):
            try:
                if (self._request != None):
                    self._multi.remove_handle(self._request._get_curl())
                self._multi.close()
            except:
                pass
            self._multi = None
            self._state = AsyncTask.STATE_CLOSED

        if (self._request != None):
            self._request.close()
            self._request = None

    @_state_locked
    def get_state(self):
        return self._state

    @_state_locked
    def cancel(self):
        """
        実行中のリクエストをキャンセルする。転送は中断され、failが呼ばれる。
        """
        self._cancel()

    def wait(self):
        """
        リクエストの終了を待機する。
        """
        self._thread.join()

    @_state_locked
    def _start(self):
        if (self._state != AsyncTask.STATE_INIT):
            raise RuntimeError("Thread already started.")

        self._state = AsyncTask.STATE_RUNNING
        self._thread = Thread(target=self._run)
        self._thread.setDaemon(True)
        self._thread.start()

    def _run(self):
        m = self._multi

        try:
            count = 0
            while (1):
                ret, num_handles = m.perform()
                if (ret != pycurl.E_CALL_MULTI_PERFORM):
                    break
                # 10回ごとにstateをチェックする
                count += 1
                if (count >= 10):
                    if (self.get_state() != AsyncTask.STATE_RUNNING):
                        break
                    count = 0

            while (self.get_state() == AsyncTask.STATE_RUNNING and
                   num_handles > 0):
                ret = m.select(1.0)
                if (ret == - 1):
                    continue

                count = 0
                while (1):
                    ret, num_handles = m.perform()
                    if (ret != pycurl.E_CALL_MULTI_PERFORM):
                        break
                    # 10回ごとにstateをチェックする
                    count += 1
                    if (count >= 10):
                        if (self.get_state() != AsyncTask.STATE_RUNNING):
                            break
                        count = 0
        except:
            pass

        # 成功か失敗かを判定し、コールバックする
        num_queued, success, failure = m.info_read()
        req = self._request
        self._multi.remove_handle(req._get_curl())
        self._request = None
        if (len(success) > 0):
            self._succeed(Response(req))
        elif (len(failure) > 0):
            self._fail(Response(req), failure[0][1], failure[0][2])
        else:
            self._fail(None, 0, "Request aborted.")

        # 状態を変更
        self._state_lock.acquire()
        self._state = AsyncTask.STATE_STOPPED
        self._state_lock.release()

    def _cancel(self):
        # ストップフラグを立てる
        if (self._state == AsyncTask.STATE_RUNNING):
            self._state = AsyncTask.STATE_STOPPING
        else:
            return

        # スレッドが止まるまで待機
        self._thread.join()
        self._thraed = None
        self._state = AsyncTask.STATE_STOPPED


class ConnectionError(Exception):
    _response = None
    _code = None

    def __init__(self, res, code, msg):
        Exception.__init__(self, msg)
        self._response = res
        self._code = code

    def get_response(self):
        """
        エラー原因となったResponseオブジェクトを取得する。
        """
        return self._response

    def get_code(self):
        """
        エラー原因を示すpycurlのエラーコードを取得する。
        """
        return self._code


def request(url, connect_timeout=None, timeout=None,
            max_header_size=None, max_body_size=None):
    """
    指定したURLを取得する。取得が完了するか、エラーが発生するまでブロックする。
    
    @param url: 取得するURLあるいはRequestオブジェクト
    @type url: Request/str/unicode
    @param connect_timeout: 接続タイムアウト秒数
    @type connect_timeout: int
    @param timeout: トータルのタイムアウト秒数
    @type timeout: int
    @param max_header_size: サーバから受信するヘッダの最大サイズ
    @type max_header_size: int/long
    @param max_body_size: サーバから受信するメッセージボディの最大サイズ
    @type max_body_size: int/long
    @return: tuple (response, content)
             responseは、Responseオブジェクト
             contentは、レスポンスメッセージボディ(str)
    @raise ConnectionError: 取得に失敗
    @note:
        404応答や500応答は、成功とみなされる。
        ConnectionErrorは、通信断やタイムアウトによりraiseされる。
    """
    result = [None, None, None]

    def succeed(res):
        result[0] = res
        result[1] = res.get_content()

    def fail(res, errcode, errmsg):
        result[2] = ConnectionError(res, errcode, errmsg)

    task = request_async(url, succeed, fail,
                         connect_timeout, timeout,
                         max_header_size, max_body_size)
    task.wait()

    if (result[2] != None):
        raise result[2]
    else:
        return result[0], result[1]


def request_async(url, succeed, fail,
                  connect_timeout=None, timeout=None,
                  max_header_size=None, max_body_size=None):
    """
    指定したURLを取得する。非同期的に動作する。
    
    @param url: 取得するURLあるいはRequestオブジェクト
    @type url: Request/str/unicode
    @param succeed: 成功した場合に呼ばれる関数。
                    シグニチャはAsyncTask#__init__を見よ。
    @type succeed: function
    @param fail: 失敗した場合に呼ばれる関数。
                 シグニチャはAsyncTask#__init__を見よ。
    @type fail: function
    @param connect_timeout: 接続タイムアウト秒数
    @type connect_timeout: int
    @param timeout: トータルのタイムアウト秒数
    @type timeout: int
    @param max_header_size: サーバから受信するヘッダの最大サイズ
    @type max_header_size: int/long
    @param max_body_size: サーバから受信するメッセージボディの最大サイズ
    @type max_body_size: int/long
    @return: 非同期タスクオブジェクト。
    @rtype: AsyncTask
    @raise ConnectionError: 取得に失敗した場合
    @note:
        404応答や500応答は、成功とみなされる。
        ConnectionErrorは、通信断やタイムアウトによりraiseされる。
    """
    if (isinstance(url, Request)):
        req = url
    else:
        req = Request(url)
    if (connect_timeout != None):
        req.set_connect_timeout(connect_timeout)
    if (timeout != None):
        req.set_timeout(timeout)
    if (max_header_size != None):
        req.set_max_header_size(max_header_size)
    if (max_body_size != None):
        req.set_max_body_size(max_body_size)

    return AsyncTask(req, succeed, fail)


def parse_http_headers(msgheaders):
    """
    Response#get_headersで返される文字列を解析し、
    [(statusline, [(name, value), (name, value), ...]), ...]
    の形式で返す。ただし、
        statusline:
          HTTPレスポンスメッセージのステータスライン
        name:
          フィールド名
        value:
          フィールド値
    
    @param msgheaders: Response#get_headers()で返される文字列
    @type msgheaders: str
    """
    headers_list = [] # [(statusline, [(name, value), ...]), ...]
    status_line = None
    headers = [] # [(name, value), ...]
    lines = msgheaders.splitlines()
    for line in lines:
        line = line.rstrip("\r\n")
        if (line.upper().startswith("HTTP/")):
            status_line = line
        elif (line == ""):
            headers_list.append((status_line, headers))
            status_line = None
            headers = []
        else:
            sep = line.find(":")
            name = line[:sep].strip()
            value = line[sep + 1:].lstrip()
            headers.append((name, value))

    return headers_list


if (__name__ == "__main__"):
    import sys
    res, content = request(sys.argv[1], 10, 30)
    print parse_headers(res.get_headers())
    print content
